package com.hoan.bitmap;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Debug;
import android.util.Log;
import android.widget.ImageView;

import androidx.annotation.Nullable;


public class BitmapNormalActivity extends Activity {
    private static final String TAG = "BitmapNormalActivity";
    private ImageView image;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_bitmap_normal);

        initView();
    }

    public void initView() {
        image = findViewById(R.id.image);
        /**
         * 微信的算法解析
         * 第一步进行采样率压缩
         * 第二步进行宽高的等比例压缩（微信对原图和缩略图限制了最大长宽或者最小长宽）
         * 第三步就是对图片的质量进行压缩（一般75或者70）
         * 第四步就是采用webP的格式。
         *
         *
         * Bitmap内存优化从下面五个方面进行优化
         * 1.编码 改变图片格式由ARGB_8888改为RGB_565
         * 2.采样(尺寸压缩)
         *      邻近采样的方式比较粗暴，直接选择其中的一个像素作为生成像素，另一个像素直接抛弃，这样就造成了图片变成了纯绿色，也就是红色像素被抛弃
         *      邻近采样采用的算法叫做邻近点插值算法
         *
         *      另一种方式   双线性采样（Bilinear Resampling）在 Android 中的使用方式一般有两种
         *          https://blog.csdn.net/c6E5UlI1N/article/details/121804230
         *
         *      压缩图片宽度和高度的像素点，压缩比太大，也会由于像素点降低导致图片失真严重，最后图片有高清成了马赛克
         *
         * 3.复用
         * 4.质量压缩       Bitmap.compress(CompressFormat format, int quality, OutputStream stream)
         *      1.质量压缩不会改变图片所占内存的大小，它改变的是图片存储的大小。即放到disk上的大小。
         *          质量压缩不会减少图片的像素，它是在保持像素的前提下改变图片的位深及透明度，来达到压缩图片的目的，
         *          所以quality值与最后生成的图片的大小并不是线性关系，比如大小为 300k的图片，当quality为90时，得到的图片大小并不是为270K
         *          图片的长，宽，像素都不会改变，那么bitmap所占内存大小是不会变的。
         *      2.质量压缩对png格式这种图片没有作用，因为png是无损压缩。
         *      3.质量压缩会导致图片失真
         *
         * 5.双线性采样(放缩法压缩 & createScaledBitmap)
         *
         *
         * 6.缓存
         * 7.匿名共享区
         *
         *
         * 参考资料
         *  https://www.jianshu.com/p/4178625ff70f
         *  https://www.lmlphp.com/user/16946/article/item/571057/
         *  https://blog.csdn.net/qq_31339141/article/details/104736704
         *  https://baijiahao.baidu.com/s?id=1620170783783027475&wfr=spider&for=pc
         *
         * ps
         *  图片很多时压缩的过程比较耗时，不要放到主线程执行。由于质量压缩并不会减少图片转换为bitmap时的内存消耗，避免出现OOM，建议先进行合适的尺寸压缩，然后再进一步进行质量压缩
         */
        initOriginal();
//        testCreate();
//        test1Format();
//        test2compressbySample(R.drawable.splash_drawable,1000,1000);
//        test2compressbySample(R.mipmap.splash,1000,1000);
//        test3Reuse();
//        test4Quality();
//        test5Scale();

//        testDiffMipmap();
//        testSplash();
    }

    private void testSplash() {
        /*BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;*/

//        Bitmap bitmapXXH = BitmapFactory.decodeResource(getResources(), R.mipmap.splash, options);
        Bitmap bitmapXXH = BitmapFactory.decodeResource(getResources(), R.mipmap.splash);
        Bitmap afterBitmap = Bitmap.createScaledBitmap(bitmapXXH, 1080, 1080, true);
        int allocationByteCountXXH = afterBitmap.getAllocationByteCount();
        int byteCountOriginalXXH = allocationByteCountXXH / 1024 / 1024;
        bitmapXXH = null;
        Log.d(TAG, "图片宽：" + afterBitmap.getWidth() + "，图片高：" + afterBitmap.getHeight() + " xxhdpi bitmap 原图大小：" + byteCountOriginalXXH + "m，" + allocationByteCountXXH + " B");
        Log.e(TAG, "=======================================");
//        image.setImageBitmap(afterBitmap);
        image.setBackgroundDrawable(new BitmapDrawable(getResources(), afterBitmap));
    }

    /**
     * 微信的算法解析
     * 第一步进行采样率压缩；
     * 第二步进行宽高的等比例压缩（微信对原图和缩略图限制了最大长宽或者最小长宽）；
     * 第三步就是对图片的质量进行压缩（一般75或者70）；
     * 第四步就是采用webP的格式。
     */
    private void testDiffMipmap() {

//        Bitmap bitmapDrawable = BitmapFactory.decodeResource(getResources(), R.drawable.qnyh_drawable);
        Bitmap bitmapDrawable = BitmapFactory.decodeResource(getResources(), R.drawable.splash_drawable);
        int allocationByteCountDrawable = bitmapDrawable.getAllocationByteCount();
        int byteCountOriginalDrawable = allocationByteCountDrawable / 1024 / 1024;
        Log.d(TAG, "图片宽：" + bitmapDrawable.getWidth() + "，图片高：" + bitmapDrawable.getHeight() + " drawabledpi bitmap 原图大小：" + byteCountOriginalDrawable + "m，" + allocationByteCountDrawable + " B");
        Log.e(TAG, "=======================================");
        image.setImageBitmap(bitmapDrawable);
//        image.setBackgroundDrawable(new BitmapDrawable(getResources(), bitmapDrawable));

       /* Bitmap bitmapH = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh_h);
        int allocationByteCountH = bitmapH.getAllocationByteCount();
        int byteCountOriginalH = allocationByteCountH / 1024 / 1024;
        Log.d(TAG, "图片宽：" + bitmapH.getWidth() + "，图片高：" + bitmapH.getHeight() + " hdpi bitmap 原图大小：" + byteCountOriginalH + "m，" + allocationByteCountH + " B");
        Log.e(TAG, "=======================================");
        image.setImageBitmap(bitmapH);

        Bitmap bitmapM = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh_m);
        int allocationByteCountM = bitmapM.getAllocationByteCount();
        int byteCountOriginalM = allocationByteCountM / 1024 / 1024;
        Log.d(TAG, "图片宽：" + bitmapM.getWidth() + "，图片高：" + bitmapM.getHeight() + " mdpi bitmap 原图大小：" + byteCountOriginalM + "m，" + allocationByteCountM + " B");
        Log.e(TAG, "=======================================");
        image.setImageBitmap(bitmapM);


        Bitmap bitmapXH = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh_xh);
        int allocationByteCountXH = bitmapXH.getAllocationByteCount();
        int byteCountOriginalXH = allocationByteCountXH / 1024 / 1024;
        Log.d(TAG, "图片宽：" + bitmapXH.getWidth() + "，图片高：" + bitmapXH.getHeight() + " xhdpi bitmap 原图大小：" + byteCountOriginalXH + "m，" + allocationByteCountXH + " B");
        Log.e(TAG, "=======================================");
        image.setImageBitmap(bitmapXH);


        Bitmap bitmapXXH = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh);
//        Bitmap bitmapXXH = BitmapFactory.decodeResource(getResources(), R.mipmap.splash);
        long allocationByteCountXXH = bitmapXXH.getAllocationByteCount();
        float byteCountOriginalXXH = allocationByteCountXXH / 1024.0f / 1024.0f;
        Log.d(TAG, "图片宽：" + bitmapXXH.getWidth() + "，图片高：" + bitmapXXH.getHeight() + " xxhdpi bitmap 原图大小：" + byteCountOriginalXXH + "m，" + allocationByteCountXXH + " B");
        Log.e(TAG, "=======================================");
        image.setImageBitmap(bitmapXXH);
*/

        /*Bitmap bitmapXXXH = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh_xxxh);
        int allocationByteCountXXXH = bitmapXXXH.getAllocationByteCount();
        int byteCountOriginalXXXH = allocationByteCountXXXH / 1024 / 1024;
        Log.d(TAG, "图片宽：" + bitmapXXXH.getWidth() + "，图片高：" + bitmapXXXH.getHeight() + " xxxhdpi bitmap 原图大小：" + byteCountOriginalXXXH + "m，" + allocationByteCountXXXH + " B");
        Log.e(TAG, "=======================================");
        image.setImageBitmap(bitmapXXXH);

        try {
            String path = Environment.getExternalStorageDirectory() + "/AData";
            File picFile = new File(path, "/qnyh_stream.jpg");
            Bitmap bitmapStream = BitmapFactory.decodeStream(new FileInputStream(picFile));
            long allocationByteCountStream = bitmapStream.getAllocationByteCount();
            float byteCountOriginalStream = allocationByteCountStream / 1024.0f / 1024.0f;
            Log.d(TAG, "图片宽：" + bitmapStream.getWidth() + "，图片高：" + bitmapStream.getHeight() + " Stream bitmap 原图大小：" + byteCountOriginalStream + "m，" + allocationByteCountStream + " B");
            Log.e(TAG, "=======================================");
            image.setImageBitmap(bitmapStream);
        } catch (Exception e) {
            Log.e(TAG, "Exception e " + e);
        }*/

    }

    private void test5Scale() {
        //1.Matrix 放缩法压缩使用的是通过矩阵对图片进行裁剪，也是通过缩放图片尺寸，来达到压缩图片的效果，和采样率的原理一样
        Matrix matrix = new Matrix();
        matrix.setScale(0.5f, 0.5f);
        Bitmap bm = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh);
        Bitmap afterBitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
        bm = null; //图片裁剪压缩后size：1m，1166400 B

        //2.createScaledBitmap 将图片的大小压缩成用户的期望大小，来减少占用内存
       /* Bitmap bm = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh);
        Bitmap afterBitmap = Bitmap.createScaledBitmap(bm, 600, 900, true);
        bm = null; //图片裁剪压缩后size：2m，2160000 B */

        int allocationByteCount = afterBitmap.getAllocationByteCount();
        int byteCountOriginal = allocationByteCount / 1024 / 1024;
        Log.e(TAG, "图片裁剪压缩后size：" + byteCountOriginal + "m，" + allocationByteCount + " B");

    }

    private void test4Quality() {
        Bitmap originBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh);
        new Thread(() -> {
            String imagePath = FileUtils.saveToFile(originBitmap, true, 140);
            //String imagePath = FileUtils.qualityCompress(originBitmap, 200);
            runOnUiThread(() -> {
                Log.d(TAG, "质量压缩 imagePath " + imagePath);
                initOriginal();
            });
        }).start();
    }

    private void test3Reuse() {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inMutable = true;
        options.inDensity = 320;
        options.inTargetDensity = 320;

        Bitmap origin = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh, options);
        image.setImageBitmap(origin);
        Log.e(TAG, origin.toString());
        Log.e(TAG, "origin:getByteCount:" + origin.getByteCount() + ",origin:getAllocationByteCount:" + origin.getAllocationByteCount());

        options.inDensity = 320;
        options.inTargetDensity = 160;
        options.inMutable = true;
        options.inBitmap = origin;

        Bitmap reuseBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh, options);
        image.setImageBitmap(reuseBitmap);
        Log.e(TAG, reuseBitmap.toString());
        Log.e(TAG, "origin:getByteCount:" + origin.getByteCount() + ",origin:getAllocationByteCount:" + origin.getAllocationByteCount());
        Log.e(TAG, "reuseBitmap:getByteCount:" + reuseBitmap.getByteCount() + ",reuseBitmap:getAllocationByteCount:" + reuseBitmap.getAllocationByteCount());
    }

    private void initOriginal() {
        Bitmap originBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.splash);
       /* int allocationByteCount = originBitmap.getAllocationByteCount();
        int byteCountOriginal = allocationByteCount / 1024 / 1024;
        Log.d(TAG, "原图大小：" + byteCountOriginal + "m，" + allocationByteCount + " B");
        Log.e(TAG, "=======================================");*/

        /*Bitmap bitmapDrawable = BitmapFactory.decodeResource(getResources(), R.drawable.splash_drawable);
        int allocationByteCountDrawable = bitmapDrawable.getAllocationByteCount();
        int byteCountOriginalDrawable = allocationByteCountDrawable / 1024 / 1024;
        Log.d(TAG, "图片宽：" + bitmapDrawable.getWidth() + "，图片高：" + bitmapDrawable.getHeight() + " drawabledpi bitmap 原图大小：" + byteCountOriginalDrawable + "m，" + allocationByteCountDrawable + " B");
        Log.e(TAG, "=======================================");*/

        image.setImageBitmap(originBitmap);
    }

    private void test2compressbySample(int drawableId, int reqWidth, int reqHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        //不获取图片，不加载到内存中，只返回图片属性,只读取图片的头信息
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), drawableId, options);
        //图片的宽高
        int outHeight = options.outHeight;
        int outWidth = options.outWidth;
        Log.d(TAG, "图片宽：" + outWidth + "，图片高：" + outHeight);

        //计算采样率
        int inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        //设置采样率，不能小于1 假如是2 则宽为之前的1/2，高为之前的1/2，一共缩小1/4 一次类推             google推荐用2的倍数
        options.inSampleSize = inSampleSize;
        Log.d(TAG, "采样率为 " + inSampleSize); //2

        //图片格式压缩
        //options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inJustDecodeBounds = false;
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), drawableId, options);
        int allocationByteCount = bitmap.getAllocationByteCount();
        int byteCountOriginal = allocationByteCount / 1024 / 1024;
        Log.e(TAG, "图片采样压缩后size：" + byteCountOriginal + "m，" + allocationByteCount + " B");//图片样压缩后size：1m，1166400B
    }

    //采样压缩比例
    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        int originalWidth = options.outWidth;
        int originalHeight = options.outHeight;

        int inSampleSize = 1;
        if (originalHeight > reqHeight || originalWidth > reqHeight) {
            // 计算出实际宽高和目标宽高的比率
            final int heightRatio = Math.round((float) originalHeight / (float) reqHeight);
            final int widthRatio = Math.round((float) originalWidth / (float) reqWidth);
            // 选择宽和高中最小的比率作为inSampleSize的值，这样可以保证最终图片的宽和高
            // 一定都会大于等于目标的宽和高。
            inSampleSize = Math.min(heightRatio, widthRatio);
        }
        return inSampleSize;
    }

    private void test1Format() {
        /**
         * 一般通过修改Bitmap的编码格式为RGB_565来达到压缩目的时，不建议修改为ARGB_4444，图片失真严重
         *
         * RGB_565 内存减少一半
         *
         * ps：虽然ARGB_4444字面上也是占2个字节  但内部实现从api19换成ARGB_8888 故内存占用和ARGB_8888是一样的
         *
         * 测试：
         * 原始图片 1080 * 1080
         *
         * 结果：
         * 原图大小：4m，4665600B
         * 压缩后大小：2m，2332800B
         */

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        Bitmap compressBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh, options);
        int allocationByteCountFormat = compressBitmap.getAllocationByteCount();
        int byteCountFormat = allocationByteCountFormat / 1024 / 1024;
        Log.e(TAG, "压缩后大小：" + byteCountFormat + "m，" + allocationByteCountFormat + "B");
    }

    private void testCreate() {
        /**
         * 不同的 Android 版本对于 Bitmap 的内存管理策略是不一样的
         * 1. Android 2.3.3 以及更低的版本，Bitmap 的像素数据存储在 Native 内存中
         * 2. Android 3.0 ~ Android 7.1，则是存储在 Dalvik 堆上，即虚拟机 Java 内存中
         * 3. Android 8.0 以及更高的版本，又重新存储在 Native 内存中
         *
         * 创建200m图片，验证下内存分配情况
         */
        Bitmap originalBitmap = Bitmap.createBitmap(1024, 1024 * 50, Bitmap.Config.ARGB_8888);
        int byteCountoriginal = originalBitmap.getAllocationByteCount() / 1024 / 1024;
        Log.e(TAG, "图片格式压缩前size大小 " + byteCountoriginal);//图片格式压缩前size大小 2278
    }

    private void test565() {
        Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh);
        int byteCountOriginal = originalBitmap.getByteCount() / 1024 / 1024;
        Log.e(TAG, "图片格式压缩前size大小 " + byteCountOriginal);//图片格式压缩前size大小 2278

        BitmapFactory.Options options = new BitmapFactory.Options();
        //不获取图片，不加载到内存，只返回图片属性
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh, options);
        //图片的宽高
        int outWidth = options.outWidth;
        int outHeight = options.outHeight;
        Log.e(TAG, "图片宽 " + outWidth + "，图片高 " + outHeight);//图片宽 1080图片高 1080
        //图片格式压缩
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inJustDecodeBounds = false;
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.qnyh, options);
        int byteCount = bitmap.getByteCount() / 1024 / 1024;
        Log.e(TAG, "图片格式压缩后size大小 " + byteCount);//图片格式压缩后size大小 1139


        image.setImageBitmap(bitmap);
    }

    private void lowMemory() {
        ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
        activityManager.getMemoryInfo(memoryInfo);

        long availMem = memoryInfo.availMem / 1024 / 1024;
        boolean lowMemory = memoryInfo.lowMemory;
        long getNativeHeapAllocatedSize = Debug.getNativeHeapAllocatedSize() / 1024 / 1024;
        Log.e(TAG, "availMem  " + availMem);//
        Log.e(TAG, "lowMemory  " + lowMemory);//
        Log.e(TAG, "getNativeHeapAllocatedSize " + getNativeHeapAllocatedSize);//
    }

}